class Matrix3 extends Array{
	constructor(copy){
		super(9);
		if (copy){
			for (let i=copy.length-1; i>=0; --i)
				this[i] = copy[i];
		}else{
			this.fill(0);
			this[0] = this[4] = this[8] = 1;
		}
	}
	
	//输出
	print(){
		let str = "[";
		for (let y=0; y<3; ++y){
			str += "\n\t";
			for (let x=0; x<3; ++x)
				str += this[3*y + x] + ", ";
		}
		console.log(str + "\n]")
		return this;
	}
	
	//克隆
	clone(){
		return new this.constructor(this);
	}
	
	//矩阵运算
	add(mat){
		for (let i=0; i<9; ++i)
			this[i] += mat[i];
		return this;
	}
	
	sub(mat){
		for (let i=0; i<9; ++i)
			this[i] -= mat[i];
		return this;
	}
	
	multiply(mat){
		const a00 = this[0 * 3 + 0],
			a01 = this[0 * 3 + 1],
			a02 = this[0 * 3 + 2],
			a10 = this[1 * 3 + 0],
			a11 = this[1 * 3 + 1],
			a12 = this[1 * 3 + 2],
			a20 = this[2 * 3 + 0],
			a21 = this[2 * 3 + 1],
			a22 = this[2 * 3 + 2],
			b00 = mat[0 * 3 + 0],
			b01 = mat[0 * 3 + 1],
			b02 = mat[0 * 3 + 2],
			b10 = mat[1 * 3 + 0],
			b11 = mat[1 * 3 + 1],
			b12 = mat[1 * 3 + 2],
			b20 = mat[2 * 3 + 0],
			b21 = mat[2 * 3 + 1],
			b22 = mat[2 * 3 + 2];
		
		this[0] = b00 * a00 + b01 * a10 + b02 * a20,
		this[1] = b00 * a01 + b01 * a11 + b02 * a21,
		this[2] = b00 * a02 + b01 * a12 + b02 * a22,
		this[3] = b10 * a00 + b11 * a10 + b12 * a20,
		this[4] = b10 * a01 + b11 * a11 + b12 * a21,
		this[5] = b10 * a02 + b11 * a12 + b12 * a22,
		this[6] = b20 * a00 + b21 * a10 + b22 * a20,
		this[7] = b20 * a01 + b21 * a11 + b22 * a21,
		this[8] = b20 * a02 + b21 * a12 + b22 * a22;
		return this;
	}
	
	//标量运算
	addScale(scale){
		for (let i=0; i<9; ++i)
			this[i] += scale;
		return this;
	}
	
	subScale(scale){
		for (let i=0; i<9; ++i)
			this[i] -= scale;
		return this;
	}
	
	multipleScale(scale){
		for (let i=0; i<9; ++i)
			this[i] *= scale;
		return this;
	}
	
	divideByScale(scale){
		for (let i=0; i<9; ++i)
			this[i] /= scale;
		return this;
	}
	
	divideScale(scale){
		for (let i=0; i<9; ++i)
			this[i] = scale / this[i];
		return this;
	}
	
	//矩阵变换
	translate(tx=0, ty=0){
		return this.multiply(
			new this.constructor([
				1, 0, 0,
				0, 1, 0,
				tx, ty, 1
			])
		);
	}
	
	rotate(angle=0){
		const c = Math.cos(angle),
			 s = Math.sin(angle);
		
		return this.multiply(
			new this.constructor([
				c, -s, 0,
				s, c, 0,
				0, 0, 1
			])
		);
	}
	
	scale(sx=1, sy=1){
		return this.multiply(
			new this.constructor([
				sx, 0,	0,
				0, sy,	0,
				0,	0,	1
			])
		);
	}
}


class Matrix4 extends Array{
	constructor(copy){
		super(16);
		if (copy){
			for (let i=copy.length-1; i>=0; --i)
				this[i] = copy[i];
		}else{
			this.fill(0);
			this[0] = this[5] = this[10] = this[15] = 1;
		}
	}
	
	//输出
	print(){
		let str = "[";
		for (let y=0; y<4; ++y){
			str += "\n\t";
			for (let x=0; x<4; ++x)
				str += this[4*y + x] + ", ";
		}
		console.log(str + "\n]")
		return this;
	}
	
	//克隆
	clone(){
		return new this.constructor(this);
	}
	
	//设置
	set(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44){
		this[0] = n11, this[4] = n12, this[8] = n13, this[12] = n14,
		this[1] = n21, this[5] = n22, this[9] = n23, this[13] = n24,
		this[2] = n31, this[6] = n32, this[10] = n33, this[14] = n34,
		this[3] = n41, this[7] = n42, this[11] = n43, this[15] = n44;
		return this;
	}
	
	//矩阵运算
	add(mat){
		for (let i=0; i<16; ++i)
			this[i] += mat[i];
		return this;
	}
	
	sub(mat){
		for (let i=0; i<16; ++i)
			this[i] -= mat[i];
		return this;
	}
	
	multiply(mat){
		const b00 = mat[0 * 4 + 0], b01 = mat[0 * 4 + 1], b02 = mat[0 * 4 + 2], b03 = mat[0 * 4 + 3],
			b10 = mat[1 * 4 + 0], b11 = mat[1 * 4 + 1], b12 = mat[1 * 4 + 2], b13 = mat[1 * 4 + 3],
			b20 = mat[2 * 4 + 0], b21 = mat[2 * 4 + 1], b22 = mat[2 * 4 + 2], b23 = mat[2 * 4 + 3],
			b30 = mat[3 * 4 + 0], b31 = mat[3 * 4 + 1], b32 = mat[3 * 4 + 2], b33 = mat[3 * 4 + 3],
			a00 = this[0 * 4 + 0], a01 = this[0 * 4 + 1], a02 = this[0 * 4 + 2], a03 = this[0 * 4 + 3],
			a10 = this[1 * 4 + 0], a11 = this[1 * 4 + 1], a12 = this[1 * 4 + 2], a13 = this[1 * 4 + 3],
			a20 = this[2 * 4 + 0], a21 = this[2 * 4 + 1], a22 = this[2 * 4 + 2], a23 = this[2 * 4 + 3],
			a30 = this[3 * 4 + 0], a31 = this[3 * 4 + 1], a32 = this[3 * 4 + 2], a33 = this[3 * 4 + 3];

		this[0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30,
		this[1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31,
		this[2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32,
		this[3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33,
		this[4] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30,
		this[5] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31,
		this[6] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32,
		this[7] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33,
		this[8] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30,
		this[9] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31,
		this[10] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32,
		this[11] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33,
		this[12] = b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30,
		this[13] = b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31,
		this[14] = b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32,
		this[15] = b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33;
		return this;
	}
	
	//标量运算
	addScale(scale){
		for (let i=0; i<16; ++i)
			this[i] += scale;
		return this;
	}
	
	subScale(scale){
		for (let i=0; i<16; ++i)
			this[i] -= scale;
		return this;
	}
	
	multipleScale(scale){
		for (let i=0; i<16; ++i)
			this[i] *= scale;
		return this;
	}
	
	divideByScale(scale){
		for (let i=0; i<16; ++i)
			this[i] /= scale;
		return this;
	}
	
	divideScale(scale){
		for (let i=0; i<16; ++i)
			this[i] = scale / this[i];
		return this;
	}
	
	//矩阵变换
	translate(tx=0, ty=0, tz=0){
		return this.multiply(
			new this.constructor([
				1, 0, 0, 0,
				0, 1, 0, 0,
				0, 0, 1, 0,
				tx, ty, tz, 1
			])
		);
	}
	
	xRotate(angle=0){
		const c = Math.cos(angle),
			 s = Math.sin(angle);
		
		return this.multiply(
			new this.constructor([
				1, 0, 0, 0,
				0, c, s, 0,
				0, -s, c, 0,
				0, 0, 0, 1
			])
		);
	}
	
	yRotate(angle=0){
		const c = Math.cos(angle),
			 s = Math.sin(angle);
		
		return this.multiply(
			new this.constructor([
				c, 0, -s, 0,
				0, 1, 0, 0,
				s, 0, c, 0,
				0, 0, 0, 1
			])
		);
	}
	
	zRotate(angle=0){
		const c = Math.cos(angle),
			 s = Math.sin(angle);
		
		return this.multiply(
			new this.constructor([
				c, s, 0, 0,
				-s, c, 0, 0,
				0, 0, 1, 0,
				0, 0, 0, 1
			])
		);
	}
	
	scale(sx=1, sy=1, sz=1){
		return this.multiply(
			new this.constructor([
				sx, 0,	0,	0,
				0, sy,	0,	0,
				0,	0, sz,	0,
				0,	0,	0,	1
			])
		);
	}
	
	//行列式
	det(){
		return this[0] * this[5] * this[10] * this[15] +
			this[1] * this[6] * this[11] * this[12] +
			this[2] * this[7] * this[8] * this[13] +
			this[3] * this[4] * this[9] * this[14] -
			this[3] * this[6] * this[9] * this[12] -
			this[2] * this[5] * this[8] * this[15] -
			this[1] * this[4] * this[11] * this[14] -
			this[0] * this[7] * this[10] * this[13];
	}
	
	//转置
	transpose(){
		[this[1], this[4]] = [this[4], this[1]];
		[this[2], this[8]] = [this[8], this[2]];
		[this[3], this[12]] = [this[12], this[3]];
		[this[6], this[9]] = [this[9], this[6]];
		[this[7], this[13]] = [this[13], this[7]];
		[this[11], this[14]] = [this[14], this[11]];
		return this;
	}
	
	//逆矩阵
	inverse(){
		const n11 = this[0], n21 = this[1], n31 = this[2], n41 = this[3],
			n12 = this[4], n22 = this[5], n32 = this[6], n42 = this[7],
			n13 = this[8], n23 = this[9], n33 = this[10], n43 = this[11],
			n14 = this[12], n24 = this[13], n34 = this[14], n44 = this[15],
		
			t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,
			t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,
			t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,
			t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
		
		const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;
		
		if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
		
		const detInv = 1 / det;
		
		this[0] = t11 * detInv;
		this[1] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv;
		this[2] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv;
		this[3] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv;
		
		this[4] = t12 * detInv;
		this[5] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv;
		this[6] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv;
		this[7] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv;
		
		this[8] = t13 * detInv;
		this[9] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv;
		this[10] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv;
		this[11] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv;
		
		this[12] = t14 * detInv;
		this[13] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv;
		this[14] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv;
		this[15] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv;
		return this;
	}
	
	//静态方法
	static perspective(fov, aspect, near, far){
		const f = Math.tan(Math.PI * 0.5 - 0.5 * fov);
		return new this([
			f / aspect, 0, 0, 0,
			0, f, 0, 0,
			0, 0, (near + far) / (near - far), -1,
			0, 0, near * far / (near - far) * 2, 0
		]);
	}
}